package com.floretexaminaction.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.floretexaminaction.dao.ClassManageDao;
import com.floretexaminaction.dao.ExaminationRecordDao;
import com.floretexaminaction.dao.TeacherDao;
import com.floretexaminaction.model.*;
import com.floretexaminaction.redis.Redis;
import com.floretexaminaction.rsaEncrypt.RSAEncrypt;
import com.floretexaminaction.service.UserService;
import com.sun.media.jfxmediaimpl.HostUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.Jedis;
import sun.misc.BASE64Encoder;

import javax.imageio.ImageIO;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List;

@RestController
@Slf4j
public class TeacherController {

    Log log = LogFactory.getLog("三岁小仙仙");

    Jedis redis = Redis.getRedis();

    @Autowired
    TeacherDao teacherDao;

    @Autowired
    UserService userService;

    @Autowired
    ClassManageDao classManageDao;

    @Autowired
    ExaminationRecordDao examinationRecordDao;

    @Autowired
    StudentController studentController;

    /**
     * 生成验证码
     * @param request
     * @param response
     * @throws IOException
     * @return
     */
    @PostMapping("/getCode")
    private String getCode(String val, HttpServletRequest request, HttpServletResponse response) throws IOException {
        //创建图片缓冲区
        BufferedImage bi = new BufferedImage(100, 30, BufferedImage.TYPE_3BYTE_BGR);
        //在缓冲区上创建画布
        Graphics gh = bi.getGraphics();
        //设置画布背景颜色
        gh.setColor(Color.orange);
        //创建画布矩形
        gh.fillRect(0, 0, 100, 30);
        //创建随机数
        Random rand = new Random();
        //存储随机数
        int[] arr = new int[4];
        //产生四个随机数
        for (int i = 0; i < 4; i++) {
            //产生1-10的随机数存储在数组中
            arr[i] = rand.nextInt(10);
            //设置随机颜色
            gh.setColor(new Color(rand.nextInt(255), rand.nextInt(255), rand.nextInt(255)));
            //设置字体类型
            gh.setFont(new Font("", Font.BOLD, 20));
            //将随机数画在画布上
            gh.drawString(String.valueOf(arr[i]), i * 20 + 4, 20);
        }
        String code = "";
        for (int i = 0; i < arr.length; i++) {
            code += arr[i];
        }
        redis.set(code, code);
        redis.expire(code, 300);
        // 页面上直接返回图片
        //ImageIO.write(bi, "jpg", response.getOutputStream());

        // 返回Base64编码的图片
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ImageIO.write(bi, "jpg", outputStream);
        BASE64Encoder base64Encoder = new BASE64Encoder();
        String base64 = base64Encoder.encode(outputStream.toByteArray()).trim();
        base64.replaceAll("\n","").replaceAll("\r","");
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("code", "data:image/jpg;base64," + base64);

        RSAEncrypt.createRSA();
        // 加密验证码
        String encrypt = RSAEncrypt.encrypt(code, RSAEncrypt.getPubKey());
        jsonObject.put("_c",encrypt);
        // 解密上次获取的验证码 并删除
        if(!val.equals("")){
            String decrypt = RSAEncrypt.decrypt(val.toString(), RSAEncrypt.getPrvKey());
            redis.del(decrypt);
        }
        gh.dispose();
        return jsonObject.toString();
    }

    /**
     * 登录页获取公钥
     * @return
     */
    @GetMapping("/getPubKey")
    public String getPubKey(){
        String pubKey = RSAEncrypt.getPubKey();
        return pubKey;
    }

    /**
     * 教师/学生登录
     * @param userid 教师账号
     * @param userpass 教师密码
     * @param code 验证码
     * @return
     */
    @PostMapping("/login")
    public String[] login(String userid, String userpass, boolean type, String code){
        JSONObject jsonObject = JSONObject.parseObject(code);
        log.info(jsonObject);
        if(!redis.exists(jsonObject.get("code").toString())){
            if (!jsonObject.get("_c").toString().equals("")){
                String decrypt = RSAEncrypt.decrypt(jsonObject.get("_c").toString(), RSAEncrypt.getPrvKey());
                redis.del(decrypt);
            }
            return new String[]{"0"}; // 验证码错误
        }
        if (type){
            return teacherLogin(userid, userpass, jsonObject.get("code").toString());
        }else{
            return studentLogin(userid, userpass, jsonObject.get("code").toString());
        }
    }

    /**
     * 教师登录调用
     * @param username 教师工号
     * @param userpass 教师密码
     * @param code 验证码
     * @return 1（用户账号或密码错误） 或 2（登录成功）
     */
    private String[] teacherLogin(String username, String userpass, String code){
        Optional<TeacherEntity> byId = teacherDao.findById(username);
        // 用户账号或密码错误 其实是用户不存在
        if(byId.isPresent()){
            String teacherPass = byId.get().getTeacherPass();
            if(teacherPass.equals("123456")){// 若为初始密码，需要进行md5, 再后和私钥解密后的密码匹配
                teacherPass = DigestUtils.md5Hex(teacherPass);
            }
            String decrypt = RSAEncrypt.decrypt(userpass, RSAEncrypt.getPrvKey());
            if (teacherPass.equals(decrypt)){
                redis.del(code);
                return new String[]{"2", byId.get().getTeacherName()};
            }
        }
        redis.del(code);
        return new String[]{"1"};
    }

    /**
     * 学生登录调用
     * @param username 学号
     * @param userpass 密码
     * @param code 验证码
     * @return 1（用户账号或密码错误） 或 2（登录成功）
     */
    private String[] studentLogin(String username, String userpass, String code){
        String classNumber = username.substring(0, 7);
        String classStudent = userService.queryClassStudent(classNumber, username);
        List<StudentEntity> studentEntities = JSONArray.parseArray(classStudent, StudentEntity.class);

        String studentPass = null;
        if(studentEntities.size() != 0){
            studentPass = studentEntities.get(0).getStudentPass();
        }
        if(studentEntities.size() == 0){// 未找到用户
            redis.del(code);
            return new String[]{"1"}; // 用户名或密码错误
        }else if(studentPass.equals("123456")){
            studentPass = DigestUtils.md5Hex(studentEntities.get(0).getStudentPass());
        }

        String decrypt = RSAEncrypt.decrypt(userpass, RSAEncrypt.getPrvKey());
        if(studentPass.equals(decrypt)){
            redis.del(code);
            return new String[]{"2", studentEntities.get(0).getStudentName()};
        }
        redis.del(code);
        return new String[]{"1"};
    }

    /**
     * 创建考试课程
     * @param data 课程所属班级
     * @return true或错误消息
     */
    @PostMapping("/createCoursesClass")
    public String createCoursesClass(String data){
        // 添加教师创建考试记录 教师工号，班级号，课程名，开始时间，结束时间
        ExaminationRecordEntity recordEntity = JSONObject.parseObject(data, ExaminationRecordEntity.class);
        String message = userService.teacherAddCourses(recordEntity.getClassNumber(), recordEntity.getCoursesName(), true);
        if(message.equals("true")){
            int count = (int)examinationRecordDao.count();
            recordEntity.setId(++count);
            examinationRecordDao.save(recordEntity);
            if(redis.exists(recordEntity.getClassNumber())){
                String examinationTime = redis.get(recordEntity.getClassNumber());
                JSONArray jsonArray = JSONArray.parseArray(examinationTime);
                JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(recordEntity, SerializerFeature.WriteMapNullValue));
                jsonArray.add(jsonObject);
                redis.set(recordEntity.getClassNumber(), JSON.toJSONString(jsonArray));
            }else{
                // 实体类 转 json对象
                JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(recordEntity, SerializerFeature.WriteMapNullValue));
                // json对象 转 json数组对象 存jedis
                StringBuffer jsonArray = new StringBuffer("[");
                jsonArray.append(jsonObject.toJSONString());
                jsonArray.append("]");
                redis.set(recordEntity.getClassNumber(), jsonArray.toString());
            }
        }
        return message;
    }

    /**
     * 根据教师工号查询教师创建的考试记录
     * @param teacherNumber 教师工号
     * @return 数组对象
     */
    @GetMapping("/autoGetExaminationRecord")
    public String autoGetExaminationRecord(String teacherNumber){
        String createExamination = userService.findTeacherCreateExamination(teacherNumber);
        JSONArray jsonArray = JSONArray.parseArray(createExamination);
        for (int i = 0 ; i < jsonArray.size() ; i++){
            JSONObject jsonObject = JSONObject.parseObject(jsonArray.get(i).toString());
            int classPeopleNumber = userService.countClassPeopleNumebr(jsonObject.getString("classNumber"));
            jsonObject.put("classPeopleNumber", classPeopleNumber);
            jsonArray.set(i, jsonObject);
        }
        return JSON.toJSONString(jsonArray);
    }

    /**
     * 根据教师工号、班级号、课程名删除对应的考试记录
     * @param data 封装数据
     * @return 删除数量 正常情况为1或0
     */
    @PostMapping("/deleteExamination")
    public int deleteExamination(String data){
        JSONObject jsonObject = JSONObject.parseObject(data);
        String teacherNumber = jsonObject.get("teacherNumber").toString();
        String classNumber = jsonObject.get("classNumber").toString();
        String coursesName = jsonObject.get("coursesName").toString();
        String questionBank = jsonObject.get("questionBank").toString();
        // 1.删除考试记录表中的数据
        int deleteExamination = userService.deleteExamination(teacherNumber, classNumber, coursesName);
        // 2.删除班级中所属性的对应考试课程名
        String courses = userService.teacherAddCourses(classNumber, coursesName, false);
        // 3.判断用户是否已录入过试题库，若存在则删除对应试题库，若无则不做处理
        if(!questionBank.equals("null")){
            userService.deleteClass(questionBank);
            userService.deleteClass(classNumber+"-"+questionBank);
            redis.del(questionBank);
            redis.del(classNumber+"-"+questionBank);
        }
        // 4.删除对应redis缓存中的数据
        if(redis.exists(classNumber)){
            String JsonString = redis.get(classNumber);
            JSONArray jsonArray = JSONArray.parseArray(JsonString);
            for (int i = 0 ; i < jsonArray.size() ; i++){
                JSONObject object = JSONObject.parseObject(jsonArray.get(i).toString());
                if(object.get("teacherNumber").equals(teacherNumber) && object.get("coursesName").equals(coursesName)){
                    jsonArray.remove(i);
                }
            }
            redis.del(classNumber);
            if(jsonArray.size()==0){
                redis.del(classNumber);
            }else{
                redis.set(classNumber, jsonArray.toJSONString());
            }
        }
        return deleteExamination;
    }

    /**
     * 编辑考试记录信息 -- 这里的编辑考试记录是修改考试时间
     * @param data 封装对象
     * @return 更新数
     */
    @PostMapping("/editExamination")
    public int editExamination(String data) throws ParseException {
        JSONObject jsonObject = JSONObject.parseObject(data);
        String teacherNumber = jsonObject.get("teacherNumber").toString();
        String classNumber = jsonObject.get("classNumber").toString();
        String coursesName = jsonObject.get("coursesName").toString();
        String startTime = jsonObject.get("startTime").toString();
        String endTime = jsonObject.get("endTime").toString();
        int update = userService.editExamination(teacherNumber, classNumber, coursesName, startTime, endTime);
        // 不用判断缓存中是否存在对应班级号记录，该编辑方法的前提条件是 有考试记录
        String jsonString = redis.get(classNumber);
        JSONArray jsonArray = JSONArray.parseArray(jsonString);
        String questionBank = null;
        for (int i = 0 ; i < jsonArray.size(); i++){
            JSONObject object = JSONObject.parseObject(jsonArray.get(i).toString());
            if (object.get("teacherNumber").equals(teacherNumber) && object.get("coursesName").equals(coursesName)){
                Set<String> strings = object.keySet();
                for (String s:strings){
                    if (s.equals("questionBank")){
                        questionBank = object.get("questionBank").toString();
                    }
                }
                object.put("startTime", startTime);
                object.put("endTime", endTime);
                jsonArray.remove(i);
                jsonArray.add(i, object);
            }
        }
        redis.set(classNumber, jsonArray.toJSONString());
        // 既然编辑了考试时间，则 对应的试题库过期时间也需要更新，但，这也有一个可能，教师根本没有创建试题库，却修改了
        // 考试时间，所以在这里需要判断是否存在试题库, 如何获取，根据更新的考试记录信息中获取 试题库号，若有则存在试题库号
        if(questionBank != null){ // 在考试记录表中，记录有一个试题库
            if(redis.exists(questionBank)){ // 双重判断一下，表中有记录，怕万一缓存里没有
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
                Date parse = sdf.parse(endTime);
                // 3.计算出来是毫秒，实际需要秒单位 / 1000 , 最后四舍五入
                long effectiveTime = Math.round((parse.getTime() - new Date().getTime()) / 1000);
                redis.expire(questionBank, (int) effectiveTime);
            }
        }

        return update;
    }

    /**
     * 添加/更新试题库
     * @param ID 教师创建的考试记录表中的对应ID号
     * @param questionsList 数组对象
     * @return true或失败消息
     */
    @PostMapping("/saveQuestionBank")
    public String saveQuestionBank(String ID, String questionsList) throws ParseException {
        List<QuestionBankEntity> list = JSONArray.parseArray(questionsList, QuestionBankEntity.class);
        // 1.查询考试记录表中对应的班级试题库是否已经创建试题库
        ExaminationRecordEntity examinationRecordEntity = examinationRecordDao.findById(Integer.valueOf(ID)).get();
        // 2.计算考试结束时间，缓存中需要设置，这里提前选计算好，后面直接用
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        Date parse = sdf.parse(examinationRecordEntity.getEndTime());
        // 3.计算出来是毫秒，实际需要秒单位 / 1000 , 最后四舍五入
        long effectiveTime = Math.round((parse.getTime() - new Date().getTime()) / 1000);
        if(examinationRecordEntity.getQuestionBank() == null){//考试记录表中没有试题库记录
            // 4.创建随机试题库号
            Random random = new Random();
            StringBuffer questionBank = new StringBuffer();
            for(int i = 0 ; i < 10 ; i++){
                questionBank.append(random.nextInt(10));
            }
            // 5.创建试题库表
            String createQB = userService.createQuestionBank(questionBank.toString());
            String res = "试题创建失败";
            if(createQB.equals("true")){
                // 6.将试题信息批量插入到数据库中，持久化存储
                res = userService.saveBatch(list, questionBank.toString());
                // 7.在考试记录表中 录入本课程对应的试题库号
                examinationRecordEntity.setQuestionBank(questionBank.toString());
                examinationRecordDao.save(examinationRecordEntity);
                // 8.将试题库号录入数据库后，同时录入缓存redis中
                String jsonString = redis.get(examinationRecordEntity.getClassNumber());
                JSONArray jsonArray = JSONArray.parseArray(jsonString);
                for (int i = 0 ; i < jsonArray.size() ; i++){
                    JSONObject jsonObject = JSONObject.parseObject(jsonArray.get(i).toString());
                    if(jsonObject.get("teacherNumber").equals(examinationRecordEntity.getTeacherNumber()) && jsonObject.get("coursesName").equals(examinationRecordEntity.getCoursesName())){
                        jsonObject.put("questionBank", examinationRecordEntity.getQuestionBank());
                        jsonArray.remove(i);
                        jsonArray.add(i, jsonObject);
                    }
                }
                redis.set(examinationRecordEntity.getClassNumber(), JSON.toJSONString(jsonArray));
                // 分隔线------------下面的set 和 上面的set是对不一样的缓存key操作，不要看错了
                // 9.将试题库存入缓存redis中, 此处不用判断该试题库是否存在，因为该试题库与数据库一一对应
                redis.set(examinationRecordEntity.getQuestionBank(), JSON.toJSONString(list));
                // 10.设置一下过期时间，就是考试结束时间
                redis.expire(examinationRecordEntity.getQuestionBank(), (int) effectiveTime + 600);
            }
            return res;
        }else{
            // 教师操作了 更新试题库
            // 1.这里为了简单，先删除对应试题库中所有数据
            String deleteInfo = userService.deleteQuestionBank(examinationRecordEntity.getQuestionBank());
            // 2.判断一下是否删除成功
            if(!deleteInfo.equals("true")){return deleteInfo;};
            // 3.将获取到的最新试题信息重新批量插入到试题库表中
            String updateQuestionBank = userService.saveBatch(list, examinationRecordEntity.getQuestionBank());
            // 4.重新将信息存入缓存中, 判断一下保存成功没有
            if(updateQuestionBank.equals("true")){
                // 5.执行到了这一步，不用判断是否存在缓存中的对应试题库了，因为，这是在有试题库的情况下才进入到这里的else
                redis.del(examinationRecordEntity.getQuestionBank());
                // 6.直接将list数组对象，转换为字符数组对象
                redis.set(examinationRecordEntity.getQuestionBank(), JSON.toJSONString(list));
                // 7.设置一下过期时间，就是考试结束时间
                redis.expire(examinationRecordEntity.getQuestionBank(), (int) effectiveTime + 600);
            }
            // 8.返回用户操作情况
            return updateQuestionBank.equals("true")?"试题更新成功":updateQuestionBank;
        }
    }

    /**
     * 查询试题库所有试题
     * @param questionBankName 试题库名
     * @return tableAll
     */
    @PostMapping("/autoGetQuestionBankInfo")
    public String autoGetQuestionBankInfo(String questionBankName){
        // 根据试题库号查询试题库所有信息，有两种方法：
        // 1.读取缓存中的试题库中的信息，当前若是存在的话（因为第一个试题库都有过期时间），这样可以更快的读取记录
        // 2.读取数据库中的记录信息，当缓存中不存在的时候，就进数据库读取
        // 3.若都没有处理返回信息
        String questionBankInfo = null;
        if(redis.exists(questionBankName)){
            questionBankInfo = redis.get(questionBankName);
        }else{
            questionBankInfo = userService.queryClassStuFindNumber(questionBankName);
        }
        List<QuestionBankEntity> list = JSONArray.parseArray(questionBankInfo, QuestionBankEntity.class);
        return JSON.toJSONString(list);
    }

    /**
     * 根据试题库名，删除试题库中所有试题
     * @param questionBankName 试题库名
     */
    @PostMapping("/clearAllQuestionBank")
    public String clearAllQuestionBank(String questionBankName){
        String deleteInfo = userService.deleteQuestionBank(questionBankName);
        String jsonString = redis.get(questionBankName);
        JSONArray jsonArray = JSONArray.parseArray(jsonString);
        jsonArray.clear();
        redis.set(questionBankName, JSON.toJSONString(jsonArray));
        return deleteInfo.equals("true")?"删除成功":deleteInfo;
    }

    /**
     * 获取班级学生科目成绩
     * @param classNumber 班级号
     * @param coursesName 科目
     * @return json数组
     */
    @PostMapping("/autoGetClassStudentCoursesResult")
    public String autoGetClassStudentCoursesResult(String classNumber, String coursesName){
        String jsonString = userService.autoGetClassStudentCoursesResult(classNumber, coursesName);
        return jsonString;
    }

}
